<template>
	<div class="base-dropdown">
		<n-popover
			:show="_show"
			trigger="click"
			:show-arrow="false"
			:width="filterable && !!minWidth ? minWidth : width"
			placement="bottom"
			style="--n-space: 0"
			@update:show="onClickShow()"
			v-bind="popoverProps">
			<template #trigger>
				<div
					class="cursor-pointer"
					:class="{ '!pointer-events-none': readonly, '!cursor-not-allowed': disabled }">
					<slot :data="data" :show="_show"></slot>
				</div>
			</template>
			<div class="rounded-lg overflow-hidden">
				<div v-if="border" class="h-[2px] bg-primary" />
				<div v-if="filterable || !!title || !!titleT" class="p-2">
					<base-text :text="title" :textT="titleT" block class="mb-[6px] text-t-1" />
					<base-search v-if="filterable" v-model:value="keywords" clearable size="large" />
				</div>
				<n-scrollbar class="max-h-[300px]">
					<template v-if="!!filteredOptions.length">
						<div
							v-for="item in filteredOptions"
							:key="item.id"
							class="h-12 flex items-center px-3 app-bg-react"
							:class="[{ 'bg-primary': item.id === modelValue }, itemClass]"
							@click="onSelect(item)">
							<slot name="content" :item="item" :selected="item.id === modelValue">
								<base-image
									v-if="item?.icon"
									:src="item?.icon"
									class="w-5 h-5 mr-2"
									:class="iconClass" />
								<base-image
									v-if="item?.iconImg || item?.img"
									:src="item?.iconImg || item?.img"
									class="w-5 h-5 mr-2"
									:class="iconClass"
									type="server" />
								<base-image
									v-if="item?.iconSrc"
									:src="item?.iconSrc"
									class="w-5 h-5 mr-2"
									:class="iconClass"
									type="network" />
								<n-ellipsis
									v-bind="NBind.Ellipsis"
									:tooltip="isDesktop && NBind.Ellipsis.tooltip"
									class="flex-auto w-[1px]">
									<base-text class="flex-1 text-t-1" :text="item.text" :textT="item.textT" />
								</n-ellipsis>
								<div class="w-5">
									<base-icon v-if="item.id === modelValue" name="tick" class="text-t-1" size="20" />
								</div>
							</slot>
						</div>
					</template>
					<base-empty v-else class="min-h-12" empty-img="" />
					<div :class="{ 'h-2': filterable }" />
				</n-scrollbar>
			</div>
		</n-popover>
	</div>
</template>

<script setup lang="ts">
import { computed, PropType, ref, watch } from 'vue'
import { filter, find, isEqual, some } from 'lodash-es'
import { NEllipsis, NPopover, NScrollbar, PopoverProps } from 'naive-ui'
import BaseEmpty from '@/components/base/empty/index.vue'
import BaseIcon from '@/components/base/icon/index.vue'
import BaseImage from '@/components/base/image/index.vue'
import BaseSearch from '@/components/base/search/index.vue'
import BaseText from '@/components/base/text/index.vue'
import { NBind } from '@/plugins/naive-ui'
import { isDesktop } from '@/hooks/useIsPC.ts'
import { includesIgnoreCase } from '@/utils'

export interface DropdownItemType<T = string> {
	id?: any
	text?: string
	textDisplay?: string
	textT?: string
	icon?: string
	iconImg?: string
	iconSrc?: string
	content?: string
	day?: string
	name?: string
	img?: string
	type?: (T & string) | undefined

	[P: string]: any
}

const props = defineProps({
	modelValue: {
		type: [String, Number],
		required: true
	},
	defaultValue: {
		type: [String, Number]
	},
	item: {
		type: [Object, null]
	},
	title: {
		type: String,
		default: ''
	},
	titleT: {
		type: String,
		default: ''
	},
	options: {
		type: Array<DropdownItemType>,
		default: () => []
	},
	show: {
		type: Boolean,
		default: false
	},
	filterable: {
		type: Boolean,
		default: false
	},
	filterField: {
		type: [String, Array, Function] as PropType<
			string | string[] | ((list: any[], keywords: string) => any[])
		>,
		default: 'id'
	},
	border: {
		type: Boolean,
		default: false
	},
	width: {
		type: [Number, String] as PropType<PopoverProps['width']>,
		default: 'trigger'
	},
	minWidth: {
		type: [Number, String] as PropType<PopoverProps['width']>,
		default: 260
	},
	popoverProps: {
		type: Object as PropType<PopoverProps>
	},
	itemClass: {
		type: [String, Array, Object],
		default: ''
	},
	wrapClass: {
		type: [String, Array, Object],
		default: ''
	},
	iconClass: {
		type: [String, Array, Object],
		default: ''
	},
	readonly: {
		type: Boolean,
		default: false
	},
	disabled: {
		type: Boolean,
		default: false
	},
	immediate: {
		type: Boolean,
		default: false
	}
})

const emits = defineEmits([
	'update:modelValue',
	'update:item',
	'select',
	'unableTrigger',
	'update:show'
])

const _show = ref(false)
const keywords = ref('')
const data = computed(() => find(props.options, { id: props.modelValue }))

const filteredOptions = computed((): Array<DropdownItemType> => {
	if (!props.filterable) return props.options
	if (typeof props.filterField === 'string') {
		const filterField = props.filterField
		return filter(props.options, (item) => includesIgnoreCase(item[filterField], keywords.value))
	} else if (Array.isArray(props.filterField)) {
		const keys = Array.from(props.filterField)
		return filter(props.options, (item) =>
			some(keys.map, (key) => includesIgnoreCase(item[key], keywords.value))
		)
	} else if (typeof props.filterField === 'function') {
		return props.filterField?.(props.options, keywords.value)
	}
	return props.options
})

const onSelect = (val: any) => {
	emits('update:modelValue', val?.id ?? props.modelValue)
	emits('update:item', val)
	emits('select', val?.id ?? props.modelValue, val, props.options)
	onClose()
}

const onClose = () => {
	if (_show.value) {
		_show.value = false
	}
}

const onClickShow = () => {
	if (props.disabled) {
		emits('unableTrigger')
		return false
	}
	_show.value = !_show.value
}

watch(
	() => _show.value,
	(val) => emits('update:show', val)
)
watch(
	() => props.modelValue,
	() => onSelect(data.value)
)
watch(
	() => JSON.stringify(props.options),
	(_, oldOptions) => {
		// 立即执行 默认选中options中的第一项
		if (props.immediate || props.defaultValue !== undefined) {
			if (!some(props.options, { id: props.modelValue })) {
				const item = find(props.options, { id: props.defaultValue })
				if (item) {
					onSelect(item)
				} else {
					props.options?.[0] && onSelect(props.options[0])
				}
			} else {
				const oldItem = oldOptions ? find(JSON.parse(oldOptions), { id: props.modelValue }) : null
				!isEqual(data.value, oldItem) && onSelect(data.value)
			}
		}
	},
	{ immediate: true }
)

defineExpose({ onClickShow, show: _show })
</script>

<style scoped lang="scss"></style>
